package global

import (
	"com.codingq92/go-chat/utils"
	"context"
	"github.com/go-redis/redis/v8"
	"time"
)

// 利用 Lua 脚本释放锁，防止任何客户端都能解锁
const releaseLockLuaScript = `
	if redis.call("get",KEYS[1]) == ARGV[1] then
		return redis.call("del",KEYS[1])
	else
		return 0
	end
	`

type Interface interface {
	Get() bool                //获取分布式锁
	Block(seconds int64) bool //阻塞一段时间
	Release() bool            //释放分布式锁
	ForceRelease()            //强制释放分布式锁
}

// 分布式锁对象
type distributedLock struct {
	context context.Context
	lockKey string //锁名称 redis的key
	lockVal string //锁标识 redis的value
	seconds int64  //有效期
}

// NewDistributedLock 构造分布式对象
func NewDistributedLock(name string, seconds int64) Interface {
	return &distributedLock{
		context.Background(),
		name,
		utils.RandString(16),
		seconds,
	}
}

// Get 获取锁
func (l *distributedLock) Get() bool {
	return Redis.SetNX(l.context, l.lockKey, l.lockVal, time.Duration(l.seconds)*time.Second).Val()
}

// Block 阻塞一段时间，尝试获取锁
func (l *distributedLock) Block(seconds int64) bool {
	starting := time.Now().Unix()
	for {
		if !l.Get() {
			time.Sleep(time.Duration(1) * time.Second)
			if time.Now().Unix()-seconds >= starting {
				return false
			}
		} else {
			return true
		}
	}
}

// Release 释放锁
func (l *distributedLock) Release() bool {
	luaScript := redis.NewScript(releaseLockLuaScript)
	result := luaScript.Run(l.context, Redis, []string{l.lockKey}, l.lockVal).Val().(int64)
	return result != 0
}

// ForceRelease 强制释放锁
func (l *distributedLock) ForceRelease() {
	Redis.Del(l.context, l.lockKey).Val()
}
